package com.nchu.commondevelopmentframework.common.utils;

import com.google.gson.Gson;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.Map;

/**
 * @className: JwtUtils
 * @description: Jwt工具类
 * @author: Li Chuanwei
 * @date: 2024/02/05 19:37
 * @Company: Copyright [日期] by [作者或个人]
 **/
@Component
@Data
public class JwtUtils {
    /**
     * jwt密钥
     */
    @Value("${security.jwt.tokenSecret}")
    private String tokenSecret;

    /**
     * 过期时间
     */
    @Value("${security.jwt.tokenExpireTime}")
    private long tokenExpireTime;

    /**
     * 加密密钥
     */
    private Key key;


    @PostConstruct
    public void init() {
        this.key = Keys.hmacShaKeyFor(tokenSecret.getBytes());
    }
    /**
     * 生成签名，TOKEN_EXPIRE_TIME分钟过期
     * 根据内部改造，支持6种类型，Integer, Long, Boolean, Double, String, Date
     *
     * @param map
     * @return
     */
    public String sign(String subject, Map<String, Object> map) {
        try {
            // 设置过期时间
            Date date = new Date(System.currentTimeMillis() + tokenExpireTime);

            // 创建JWT，设置头部信息
            String token = Jwts.builder()
                    .claims(map)
                    .issuedAt(new Date())
                    .expiration(date)
                    .subject(subject)
                    .header()
                    .type("JWT")
                    .and()
                    .signWith(key)
                    .compact();
            return token;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 验证给定的令牌是否正确。
     *
     * @param token 要验证的JWT令牌
     * @return 如果令牌有效则返回true，否则返回false
     */
    public boolean verify(String token) {
        try {
             Jwts.parser()
                 .verifyWith((SecretKey) key)
                 .build()
                 .parseSignedClaims(token);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * 从JWT令牌中获取声明。
     *
     * @param token JWT令牌
     * @return 声明的Map
     */
    public Claims getClaims(String token) {
        Jws<Claims> jwt;
        try {
            jwt = Jwts.parser()
                    .verifyWith((SecretKey) key)
                    .build()
                    .parseSignedClaims(token);
            return jwt.getPayload();
        } catch (JwtException ex) {
            return null;
        }
    }

    /**
     * 从JWT令牌中获取头部。
     *
     * @param token JWT令牌
     * @return 声明的Map
     */
    public JwsHeader getHeader(String token) {
        Jws<Claims> jwt;
        try {
            jwt = Jwts.parser()
                    .verifyWith((SecretKey) key)
                    .build()
                    .parseSignedClaims(token);
            return jwt.getHeader();
        } catch (JwtException ex) {
            return null;
        }
    }

    /**
     * 从JWT令牌的声明中获取字符串值。
     *
     * @param token    JWT令牌
     * @param claimKey 声明的键
     * @return 指定声明的字符串值
     */
    public String getString(String token, String claimKey) {
        Claims claims = getClaims(token);
        if (claims != null) {
            return claims.get(claimKey, String.class);
        }
        return null;
    }

    /**
     * 从JWT令牌的声明中获取对象。
     *
     * @param token     JWT令牌
     * @param claimKey  声明的键
     * @param valueType 对象的类类型
     * @param <T>       对象的类型
     * @return 指定声明的对象值
     */
    public  <T> T getObject(String token, String claimKey, Class<T> valueType) {
        Claims claims = getClaims(token);
        if (claims != null) {
            String jsonClaim = claims.get(claimKey, String.class);
            return new Gson().fromJson(jsonClaim, valueType);
        }
        return null;
    }

    /**
     * 从JWT令牌的声明中获取整数值。
     *
     * @param token    JWT令牌
     * @param claimKey 声明的键
     * @return 指定声明的整数值
     */
    public Integer getInteger(String token, String claimKey) {
        Claims claims = getClaims(token);
        if (claims != null) {
            return claims.get(claimKey, Integer.class);
        }
        return null;
    }

    /**
     * 获取JWT令牌的过期时间。
     *
     * @param token JWT令牌
     * @return 过期时间
     */
    public Date getExpiresAt(String token) {
        Claims claims = getClaims(token);
        if (claims != null) {
            return claims.getExpiration();
        }
        return null;
    }

    /**
     * 获取JWT令牌的发布时间。
     *
     * @param token JWT令牌
     * @return 发布时间
     */
    public Date getIssuedAt(String token) {
        Claims claims = getClaims(token);
        if (claims != null) {
            return claims.getIssuedAt();
        }
        return null;
    }

    /**
     * 检查JWT令牌是否过期。
     *
     * @param token JWT令牌
     * @return 如果令牌过期则返回true，否则返回false
     */
    public boolean isExpired(String token) {
        try {
            final Date expiration = getExpiresAt(token);
            if (expiration == null) {
                return true;
            }
            return expiration.before(new Date());
        } catch (ExpiredJwtException e) {
            // 处理异常（例如，记录错误）
            return true;
        }
    }

    /**
     * 通过解码JWT令牌中Base64编码的头部获取头部内容。
     *
     * @param token JWT令牌
     * @return 解码后的头部内容
     */
    public String getHeaderByBase64(String token) {
        String[] parts = token.split("\\.");
        if (parts.length >= 1) {
            byte[] headerBytes = Base64.getDecoder().decode(parts[0]);
            return new String(headerBytes);
        }
        return null;
    }

    /**
     * 通过解码JWT令牌中Base64编码的有效载荷获取有效载荷内容。
     *
     * @param token JWT令牌
     * @return 解码后的有效载荷内容
     */
    public String getPayloadByBase64(String token) {
        String[] parts = token.split("\\.");
        if (parts.length >= 2) {
            byte[] payloadBytes = Base64.getDecoder().decode(parts[1]);
            return new String(payloadBytes);
        }
        return null;
    }
}